
/**
 * 详细资料：http://self-issued.info/docs/draft-ietf-jose-json-web-encryption.html
 */

const crypto = require('crypto')
const fs = require('fs')
const base64url = require('base64-url')

// RSA公钥
const publicKeyStr = fs.readFileSync("./rsa_public_key.pem", "utf8")

/**
 * JWE是一套标准，下面是其中一种实现
 * A.1. Example JWE using RSAES-OAEP and AES GCM
 * @param {json字符串} jsonData 
 */
function jsonEncrypt(jsonData) {
   /**
    * A.1.1. JOSE Header
    * {"alg":"RSA-OAEP","enc":"A256GCM"}
    */
   let header = {
      'alg': 'RSA-OAEP',
      'enc': 'A256GCM'
   }
   /**
    * 产生AES的对称密钥，长度要跟header的enc一致
    * 1个字节=8位
    * A.1.2. Content Encryption Key (CEK)
    */
   let cek = crypto.randomBytes(32)

   /**
    * A.1.3. Key Encryption
    */
   //RSA加密cek，cek需要加密后才能传输
   let cek_enc = crypto.publicEncrypt({ key: publicKeyStr, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING }, cek);
   /**
    * A.1.4. Initialization Vector
    */
   let iv = crypto.randomBytes(12)
   /**
    * 标准规范就是Baes64Url(header)转ASCII码
    * A.1.5. Additional Authenticated Data
    */
   let headerBase64url = base64url.encode(JSON.stringify(header))
   let aad = Buffer.from(headerBase64url, 'ascii')
   /**
    * 使用AES-256-GCM进行加密
    * A.1.6. Content Encryption
    */
   let cipher = crypto.createCipheriv('aes-256-gcm', cek, iv, { authTagLength: 16 })
   cipher.setAAD(aad)
   let ciphertext = cipher.update(JSON.stringify(jsonData), 'utf8')
   cipher.final()
   // 加密结束后获取tap
   let tag = cipher.getAuthTag()
   /**
    * 完整描述，就是把结构拼接起来
    * JWE的标准格式内容
    * The JWE Compact Serialization represents encrypted content as a compact, URL-safe string. This string is:
    * 
    * BASE64URL(UTF8(JWE Protected Header)) || '.' ||
    * BASE64URL(JWE Encrypted Key) || '.' ||
    * BASE64URL(JWE Initialization Vector) || '.' ||
    * BASE64URL(JWE Ciphertext) || '.' ||
    * BASE64URL(JWE Authentication Tag)
    * A.1.7.Complete Representation
    */
   const spilt = '.'
   let jweBase64 = headerBase64url + spilt + cek_enc.toLocaleString('base64') + spilt + iv.toString('base64') + spilt + ciphertext.toString('base64') + spilt + tag.toString('base64')
   // 返回标准的Base64url格式
   return base64url.escape(jweBase64)
}


// RSA私钥
const primaryKeyStr = fs.readFileSync("./rsa_private_key.pem", "utf8")

/**
 * 解密返回json字符串
 * @param {jweCompact} jweCompact 
 */
function jsonDecrypt(jweCompact) {

   //  将base64url 将转base64 
   let base64urlArray = base64url.unescape(jweCompact).split('.')

   let header = base64urlArray[0]
   let encryptedKey = base64urlArray[1]
   let iv = base64urlArray[2]
   let ciphertext = base64urlArray[3]
   let tag = base64urlArray[4]


   let cek_enc = Buffer.from(encryptedKey,'base64')
   let cek = crypto.privateDecrypt({ key: primaryKeyStr, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING },cek_enc)


   let decipher = crypto.createDecipheriv('aes-256-gcm', cek, Buffer.from(iv,'base64'), {
      authTagLength: 16
   })

   decipher.setAAD(Buffer.from(header, "ascii"))
   decipher.setAuthTag(Buffer.from(tag,'base64'))
   let receivedPlaintext = decipher.update(Buffer.from(ciphertext,'base64'), 'utf8', 'utf8')
   try {
      decipher.final()
   } catch (err) {
      console.log('Authentication failed!')
      throw err
   }
   return receivedPlaintext
}


module.exports = {
   jsonEncrypt: jsonEncrypt,
   jsonDecrypt: jsonDecrypt
}




